package com.iot.auth.service.impl;

import java.text.ParseException;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.AccountExpiredException;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import com.alibaba.fastjson.JSONObject;
import com.iot.auth.service.IOauthService;
import com.iot.common.core.constant.RedisConstants;
import com.iot.common.core.constant.SecurityConstants;
import com.iot.common.core.util.Md5Util;
import com.iot.common.core.util.R;
import com.iot.common.core.util.WebUtils;
import com.iot.common.security.domain.SimpleUser;
import com.iot.common.security.jwt.JWTUtil;
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JWSVerifier;
import com.nimbusds.jose.crypto.MACVerifier;
import com.nimbusds.jwt.SignedJWT;

import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@Service
public class OauthServiceImpl implements IOauthService {

	private final AuthenticationManager authenticationManager;
	private final StringRedisTemplate redisTemplate;

	public void createToken(HttpServletRequest request, HttpServletResponse response, String username,
			String password) {
		try {
			Authentication authentication = authenticationManager
					.authenticate(new UsernamePasswordAuthenticationToken(username, password));
			SimpleUser user = (SimpleUser) authentication.getPrincipal();
			String payload = JSONObject.toJSONString(user);
			String token = JWTUtil.createToken(payload);

			String keyPrefix = RedisConstants.TOKEN_KEY_PREFIX + user.getId() + ":";
			String keySuffix = Md5Util.getMD5(token.getBytes());
			String key = keyPrefix + keySuffix;
			String authKey = key + RedisConstants.AUTH_KEY;

			redisTemplate.opsForValue().set(key, token, SecurityConstants.EXPIRE_TIME, TimeUnit.MILLISECONDS);
			redisTemplate.opsForValue().set(authKey, JSONObject.toJSONString(user.getAuthorities()),
					SecurityConstants.EXPIRE_TIME, TimeUnit.SECONDS);
			WebUtils.renderJson(response, R.ok("成功！", token));
		} catch (org.springframework.security.core.AuthenticationException e) {
			WebUtils.renderJson(response, R.failed("账号或密码错误！", e.getLocalizedMessage()));
		} catch (JOSEException e) {
			WebUtils.renderJson(response, R.failed("创建token异常！", e.getLocalizedMessage()));
		}
	}

	public void checkToken(HttpServletRequest request, HttpServletResponse response, String tokenHeader) {
		String token = tokenHeader.replace(SecurityConstants.TOKEN_PREFIX, "");
		try {
			SignedJWT jwt = SignedJWT.parse(token);
			JWSVerifier verifier = new MACVerifier(SecurityConstants.TOKEN_SECRET);
			if (!jwt.verify(verifier)) {
				throw new AccountExpiredException(SecurityConstants.TOKEN_INVALID);
			}
			Date expirationTime = jwt.getJWTClaimsSet().getExpirationTime();
			if (new Date().after(expirationTime)) {
				throw new AccountExpiredException(SecurityConstants.TOKEN_EXPIRE);
			}

			Object account = jwt.getJWTClaimsSet().getClaim("payload");
			if (account != null) {
				SimpleUser user = JSONObject.parseObject(account.toString(), SimpleUser.class);

				String keyPrefix = RedisConstants.TOKEN_KEY_PREFIX + user.getId() + ":";
				String keySuffix = Md5Util.getMD5(token.getBytes());
				String key = keyPrefix + keySuffix;
				String redisToken = redisTemplate.opsForValue().get(key);
				if (ObjectUtils.isEmpty(redisToken)) {
					throw new AccountExpiredException(SecurityConstants.TOKEN_CANCEL);
				}
				if (!redisToken.equals(token)) {
					WebUtils.renderJson(response, R.failed("无效token！"));
				} else {
					WebUtils.renderJson(response, R.ok("有效！", token));
				}
			} else {
				WebUtils.renderJson(response, R.failed("无效token！"));
			}
		} catch (ParseException e) {
			WebUtils.renderJson(response, R.failed("无效token！", e.getLocalizedMessage()));
		} catch (JOSEException e) {
			WebUtils.renderJson(response, R.failed("无效token！", e.getLocalizedMessage()));
		}
	}
	
	public void refreshToken(HttpServletRequest request, HttpServletResponse response, String tokenHeader) {
		String token = tokenHeader.replace(SecurityConstants.TOKEN_PREFIX, "");
		try {
			SignedJWT jwt = SignedJWT.parse(token);
			JWSVerifier verifier = new MACVerifier(SecurityConstants.TOKEN_SECRET);
			if (!jwt.verify(verifier)) {
				throw new AccountExpiredException(SecurityConstants.TOKEN_INVALID);
			}
			Date expirationTime = jwt.getJWTClaimsSet().getExpirationTime();
			if (new Date().after(expirationTime)) {
				throw new AccountExpiredException(SecurityConstants.TOKEN_EXPIRE);
			}

			Object account = jwt.getJWTClaimsSet().getClaim("payload");
			if (account != null) {
				SimpleUser user = JSONObject.parseObject(account.toString(), SimpleUser.class);

				String keyPrefix = RedisConstants.TOKEN_KEY_PREFIX + user.getId() + ":";
				String keySuffix = Md5Util.getMD5(token.getBytes());
				String key = keyPrefix + keySuffix;
				String redisToken = redisTemplate.opsForValue().get(key);
				if (ObjectUtils.isEmpty(redisToken)) {
					throw new AccountExpiredException(SecurityConstants.TOKEN_CANCEL);
				}
				if (!redisToken.equals(token)) {
					WebUtils.renderJson(response, R.failed("无效token！"));
				} else {
					WebUtils.renderJson(response, R.ok("有效！", token));
				}
			} else {
				WebUtils.renderJson(response, R.failed("无效token！"));
			}
		} catch (ParseException e) {
			WebUtils.renderJson(response, R.failed("无效token！", e.getLocalizedMessage()));
		} catch (JOSEException e) {
			WebUtils.renderJson(response, R.failed("无效token！", e.getLocalizedMessage()));
		}
	}

}
